#!/bin/bash

usage() {
  echo "Command line tool of zstack project

Usage:$0 [option]

Options:
  apihelper           generate groovy API helper

  check               static check after build finished

  deploydb            deploy a new zstack database with current schema

  deploy              deploy zstack.war to tomcat

  doc                 generate API Groovy doc templates

  docpremium          generate API Groovy doc templates (include premium)

  errorcode           generate elaboration docs

  foreignkey          generate foreign keys from Java database table entity

  git <sub-command>
                      init
                        init git templates and hooks
                      clear
                        clear git templates and hooks
                      search
                        search branch by commit info

  globalconfigdocmd   generate global config markdown docs

  i18n                generate i18n properties for zstack

  i18njson            generate JSON files containing all i18n from zstack

  md                  generate markdown docs from API Groovy doc templates

  mdpremium           generate markdown docs from API Groovy doc templates (including premium)

  openapi             generate API swagger specs (stale)

  premium             run profile under premium, build with enterprise modules

  py                  generate API Python classes and JSON templates

  pysdk               generate Python SDK

  sdk                 generate Java

  ts                  generate Typescript bindings

  xml-java-schema     deprecated: generate XML schema for Java deployer (old zstack Java test
                          case)

  zql                 generate zql ANTLR recognizer

  zwatchzql           generate zwatch zql ANTLR recognizer
"
}

MVNTest="mvn test -Djacoco.skip=true"

py() {
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi

  $MVNTest -Dtest=TestGenerateApiPythonClassAndJsonTemplate
  cd - >/dev/null
}

pysdk() {
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi

  $MVNTest -Dtest=TestGeneratePythonSDK
  cd - >/dev/null
}

apihelper() {
  cd test
  $MVNTest -Dtest=TestGenerateApiHelper
  cd - >/dev/null
  cp ~/ApiHelper.groovy testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy

  echo "ApiHelper.groovy is generated at ~/ApiHelper.groovy"
}

doc() {
  cd test
  $MVNTest -Dtest=TestGenerateDocTemplate
  cd - >/dev/null
}

openapi() {
  if [ ! -d premium/test-premium ]; then
    echo "This tool is not available in zstack of the opensource version"
    exit 1
  fi

  cd tool/doclet
  mvn -Dmaven.test.skip=true package
  cd -

  javadoc -private -doclet org.zstack.tool.doclet.JsonDocLet -docletpath tool/doclet/target/doclet-*-jar-with-dependencies.jar $(find -name *.java)

  DOC_OUTPUT=$(echo ~/zstack-doclet-output.json)
  if [ ! -f $DOC_OUTPUT ]; then
    echo "$DOC_OUTPUT not found"
    exit 1
  fi

  cd premium/test-premium
  $MVNTest -Dtest=GenerateSwaggerSpec -DdocletDoc=$DOC_OUTPUT
  cd -

  echo ""
  echo ""
  echo ""
  echo "***********************************************"
  echo "openapi spec is generated at ~/zstack-api.yaml"
  echo "***********************************************"
}

docpremium() {
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi
  $MVNTest -Dtest=TestGenerateDocTemplate -Drepair=true
  cd - >/dev/null
}

check() {
  if [ ! -d premium/test-premium ]; then
    echo "This command is provided only in the premium source code"
  fi
  cd premium/test-premium
  $MVNTest -Dtest=BasicCheckTestSuite -DskipJacoco=true
  cd - >/dev/null
}

md() {
  cd test
  $MVNTest -Dtest=TestGenerateMarkDownDoc
  cd - >/dev/null
}

mdpremium() {
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi

  $MVNTest -Dtest=TestGenerateMarkDownDoc
  cd - >/dev/null
}

errorcode() {
  cd test
  mvn test -Dtest=TestGenerateErrorCodeDoc
  cd - >/dev/null
}

i18njson() {
  ./build/zsi18n -s . -j conf/i18n.json
}

i18n() {
  ./build/zsi18n -j conf/i18n.json -d conf/i18n/
}

foreignkey() {
  set -u
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi

  $MVNTest -Dtest=TestGenerateSqlForeignKey
  cd - >/dev/null

  echo "see ~/zstack-sql/foreignKeys.sql"
}

triggerexpression() {
  if [ ! -d premium/mevoco ]; then
    echo "This command is provided only in the premium source code"
    exit 1
  fi

  cd premium/mevoco
  mvn -P trigger-expression generate-sources
  mkdir -p src/main/java/org/zstack/monitoring/trigger/expression/antlr4
  yes | cp target/generated-sources/antlr4/TriggerExpression* src/main/java/org/zstack/monitoring/trigger/expression/antlr4
  rm -f target/generated-sources/antlr4/TriggerExpression*
  cd - >/dev/null
}

zql() {
  cd search
  mvn -P zql generate-sources
  if [ $? -ne 0 ]; then
    exit 1
  fi
  mkdir -p src/main/java/org/zstack/zql/antlr4/
  yes | cp target/generated-sources/antlr4/* src/main/java/org/zstack/zql/antlr4/
  rm -f target/generated-sources/antlr4/*
  mvn -Dmaven.test.skip=true clean install
  cd - >/dev/null
}

cloudwatchfunction() {
  if [ ! -d premium/zwatch ]; then
    echo "This command is provided only in the premium source code"
    exit 1
  fi

  cd premium/zwatch
  mvn -P function generate-sources
  mkdir -p src/main/java/org/zstack/zwatch/api/antlr4/
  yes | cp target/generated-sources/antlr4/MetricFunction* src/main/java/org/zstack/zwatch/api/antlr4/
  rm -f target/generated-sources/antlr4/MetricFunction*
  cd - >/dev/null
}

zwatchzql() {
  if [ ! -d premium/zwatch ]; then
    echo "this command needs premium source code"
    exit 1
  fi

  cd premium/zwatch
  mvn -P function generate-sources
  mkdir -p src/main/java/org/zstack/zwatch/returnwith/antlr4/
  yes | cp target/generated-sources/antlr4/ReturnWith* src/main/java/org/zstack/zwatch/returnwith/antlr4/
  rm -f target/generated-sources/antlr4/ReturnWith*
  cd - >/dev/null
}

ts() {
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
    $MVNTest -Dtest=TestGenerateTypescriptBindings -Djacoco.skip=true
    cd -
  fi
}

sdk() {
  set -u
  rm -rf ~/zstack-sdk/java
  if [ -d premium/test-premium ]; then
    cd premium/test-premium
  else
    cd test
  fi

  $MVNTest -Dtest=TestGenerateSDK -Djacoco.skip=true
  if [ $? -ne 0 ]; then
    echo "if you see a compile error, you may try to compile and install the sdk module first"
    exit 1
  fi
  cd - >/dev/null

  cd sdk/src/main/java/org/zstack/sdk/
  find . -type f | grep -E -v "(ZSClient|ZSConfig|ApiException|AsyncRestState|Completion|Constants|ErrorCode|ErrorCodeList|InternalCompletion|ApiResult|Param|RestInfo|AbstractAction|QueryAction).java" | xargs rm -f
  cd -
  yes | cp -r ~/zstack-sdk/java/* sdk/src/main/java/
  set +u
}

globalconfigdocmd() {
  cd premium/test-premium
  $MVNTest -Dtest=TestGenerateGlobalConfigMarkDown -Drepair
  cd - >/dev/null
}

zprint() {
  # NOTE(weiw): `sh` in CentOS is an alias of `bash` actually
  # and `echo` behaves different for `sh` in MacOS and CentOS
  # sinec we changed shebang into bash, now it could be unify actually
  if test x$(uname) = x"Darwin"; then
    echo -e $@
  else
    echo -e $@
  fi
}

vercomp() {
  # returns: $1 > $2: return 0
  #          $2 < $1: return 1
  #          $1 = $2: return 2
  echo -e 'test' | grep -- '-e' >/dev/null
  not_suuport_e=$?
  if [[ $not_support_e = 0 ]]; then
    echo "$1\n$2" | sort --version-sort | head -n1 | grep $2 >/dev/null
    first_larger=$?
    echo "$1\n$2" | sort --version-sort | head -n1 | grep $1 >/dev/null
    second_larger=$?
  else
    echo -e "$1\n$2" | sort --version-sort | head -n1 | grep $2 >/dev/null
    first_larger=$?
    echo -e "$1\n$2" | sort --version-sort | head -n1 | grep $1 >/dev/null
    second_larger=$?
  fi
  if [[ $first_larger = 0 ]]; then
    if [[ $second_larger = 0 ]]; then
      return 2
    else
      return 0
    fi
  fi
  return 1
}

gitconfig() {
  bold=$(tput bold)
  normal=$(tput sgr0)
  if test x$2 = x; then
    zprint "\033[33mplease specify sub-command: init|clear|search\n\033[0m"

    zprint "search which branches have commits related to ZSTACK-43023: "
    zprint "\033[34m./runMavenProfile git search ZSTACK-43023 \033[0m"
    zprint "\nwill fetch remote by default, you can add --no-update to avoid update, like: "
    zprint "\033[34m./runMavenProfile git search ZSTACK-43023 --no-update \033[0m"
    zprint "\ninit git config(commit msg template and hook): just type:"
    zprint "\033[34m./runMavenProfile git init \033[0m"
    zprint "\nclear git config(commit msg template and hook): just type:"
    zprint "\033[34m./runMavenProfile git clear \033[0m"
    exit 1

  elif test x$2 = x'search'; then
    update="y"
    if test x$3 = x; then
      zprint "\033[33mplease input commit message you want to search\n\033[0m"
      zprint "search which branches have commits related to ZSTACK-43023: "
      zprint "\033[34m./runMavenProfile git search ZSTACK-43023 \033[0m"
      zprint "\nwill fetch remote by default, you can add --no-update to avoid update, like: "
      zprint "\033[34m./runMavenProfile git search ZSTACK-43023 --no-update \033[0m"
      exit 1
    fi

    if test x$4 = x"--no-update"; then
      update="n"
    fi

    zprint "============================"
    if test x$update = x'y'; then
      zprint "updating repo zstack ..."
      timeout 20 git fetch --all >/dev/null 2>&1
    fi

    zprint "searching in repo zstack ..."
    zprint "============================"
    sha1s=$(git log --oneline --all --grep "$3" | cut -d" " -f1 | tr '\n' ' ')
    if test x$(echo $sha1s | tr -d ' ') = x''; then
      zprint "\033[33mcan not find any related commits in repo zstack\033[0m"
    else
      zprint "\n\033[32mfind commits \033[34m$sha1s\033[0mrelated to \033[34m$3\033[0m \033[0m"
      for sha1 in $sha1s; do
        br=$(git branch --remote --contains $sha1 | grep -Eo '/[1-5]\.[0-9]+\.[0-9]+.*' | grep -Eo '[1-5].[0-9]+.[0-9]+.*' | grep -v '/')
        zprint "\n\033[32mrelase branches with commit \033[34m$sha1\033[0m:\033[0m \n${bold}$br ${normal}"
      done
    fi

    cd ./premium
    zprint "\n============================"
    if test x$update = x'y'; then
      zprint "updating repo premium ..."
      timeout 20 git fetch --all >/dev/null 2>&1
    fi

    zprint "searching in repo premium ..."
    zprint "============================"
    sha1s=$(git log --oneline --grep "$3" | cut -d" " -f1 | tr '\n' ' ')
    if test x$(zprint $sha1s | tr -d ' ') = x''; then
      zprint "\n\033[33mcan not find any related commits in repo premium\033[0m"
      exit 0
    fi
    zprint "\n\033[32mfind commits \033[34m$sha1s\033[0mrelated to \033[34m$3\033[0m \033[0m"
    for sha1 in $sha1s; do
      br=$(git branch --remote --contains $sha1 | grep -Eo '/[1-5]\.[0-9]+\.[0-9]+.*' | grep -Eo '[1-5].[0-9]+.[0-9]+.*' | grep -v '/')
      zprint "\n\033[32mrelease branches with commit \033[34m$sha1\033[0m:\033[0m \n${bold}$br ${normal}"
    done

  elif test x$2 = x'init'; then
    init_git_config

  elif test x$2 = x'clear'; then
    clear_git_config

  else
    zprint "\033[31mnot support ${bold}\"$2\"${normal} \033[31myet\033[0m"
  fi
}

init_git_config() {
  git config --global commit.verbose true

  message_path=$(git rev-parse --show-toplevel)/.gitconfig/gitmessage
  hook_dir=$(git rev-parse --show-toplevel)/.gitconfig/hooks

  if [ ! -f $message_path ]; then
    zprint "\033[33mERROR: can not find git message template at $message_path \033[0m"
  fi

  if [ ! -d $hook_dir ]; then
    zprint "\033[33mERROR: can not find git hook at $hook_dir \033[0m"
  fi

  curr_git_ver=$(git version | awk '{print $3}')
  vercomp $curr_git_ver 2.9
  support_config_hook=$?

  if [ $support_config_hook -eq 1 ]; then
    zprint "\033[33mgit version older than 2.9, would replace git hook in .git rather than config hook path\033[0m"
    mv $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg.bak.$(date +%s) 2>/dev/null
    mv $(git rev-parse --show-toplevel)/.git/hooks/commit-msg $(git rev-parse --show-toplevel)/.git/hooks/commit-msg.bak.$(date +%s) 2>/dev/null

    cp $(git rev-parse --show-toplevel)/.gitconfig/hooks/* $(git rev-parse --show-toplevel)/.git/hooks/
  else
    zprint "\033[33mgit version newer than 2.9, would config hook path\033[0m"
    git config core.hooksPath $(git rev-parse --show-toplevel)/.gitconfig/hooks
  fi

  git config commit.template $(git rev-parse --show-toplevel)/.gitconfig/gitmessage

  cd ./premium

  if [ $support_config_hook -eq 1 ]; then
    mv $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg.bak.$(date +%s) 2>/dev/null
    mv $(git rev-parse --show-toplevel)/.git/hooks/commit-msg $(git rev-parse --show-toplevel)/.git/hooks/commit-msg.bak.$(date +%s) 2>/dev/null

    cp $(git rev-parse --show-toplevel)/../.gitconfig/hooks/* $(git rev-parse --show-toplevel)/.git/hooks/
  else
    git config core.hooksPath $(git rev-parse --show-toplevel)/../.gitconfig/hooks
  fi

  git config commit.template $(git rev-parse --show-toplevel)/../.gitconfig/gitmessage
  zprint "\033[32mconfig git commit msg template and hook success!\033[0m"
}

clear_git_config() {
  curr_git_ver=$(git version | awk '{print $3}')
  vercomp $curr_git_ver 2.9
  support_config_hook=$?

  if [ $support_config_hook -eq 1 ]; then
    mv $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg.bak.$(date +%s) 2>/dev/null
    mv $(git rev-parse --show-toplevel)/.git/hooks/commit-msg $(git rev-parse --show-toplevel)/.git/hooks/commit-msg.bak.$(date +%s) 2>/dev/null
  else
    git config --unset core.hooksPath
  fi
  git config --unset commit.template

  cd ./premium
  if [ $support_config_hook -eq 1 ]; then
    mv $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg $(git rev-parse --show-toplevel)/.git/hooks/prepare-commit-msg.bak.$(date +%s) 2>/dev/null
    mv $(git rev-parse --show-toplevel)/.git/hooks/commit-msg $(git rev-parse --show-toplevel)/.git/hooks/commit-msg.bak.$(date +%s) 2>/dev/null
  else
    git config --unset core.hooksPath
  fi
  git config --unset commit.template

  zprint "\033[32mclear git commit msg template and hook success!\033[0m"
}

run_profile() {
  if test x$1 = x'premium'; then
    mvn -Dmaven.test.skip=true -P premium clean install
  elif test x$1 = x'md'; then
    md
  elif test x$1 = x'sdk'; then
    sdk
  elif test x$1 = x'doc'; then
    doc
  elif test x$1 = x'docpremium'; then
    docpremium
  elif test x$1 = x'check'; then
    check
  elif test x$1 = x'md'; then
    md
  elif test x$1 = x'mdpremium'; then
    mdpremium
  elif test x$1 = x'py'; then
    py
  elif test x$1 = x'pysdk'; then
    pysdk
  elif test x$1 = x'apihelper'; then
    apihelper
  elif test x$1 = x'i18njson'; then
    i18njson
  elif test x$1 = x'i18n'; then
    i18n
  elif test x$1 = x'foreignkey'; then
    foreignkey
  elif test x$1 = x'triggerexpression'; then
    triggerexpression
  elif test x$1 = x'zql'; then
    zql
  elif test x$1 = x'cloudwatchfunction'; then
    cloudwatchfunction
  elif test x$1 = x'zwatchzql'; then
    zwatchzql
  elif test x$1 = x'openapi'; then
    openapi
  elif test x$1 = x'errorcode'; then
    errorcode
  elif test x$1 = x'ts'; then
    ts
  elif test x$1 = x'globalconfigdocmd'; then
    globalconfigdocmd
  elif test x$1 = x'git'; then
    gitconfig $@
  else
    mvn -pl build -P $1 exec:exec -D$1
  fi
}

if [ "$#" -eq 0 ]; then
  usage
  exit 1
fi

if test x$1 = x'git'; then
  run_profile $@
  exit 1
fi

for cmd in $@; do
  run_profile ${cmd}
done
